/*
    文件内容: 封装Socket相关操作类
    文件名: InetAddress.cpp
    作者: 中国好青年(码农论坛)
    时间: 2024/9/5
*/

#include "Socket.h"

/* 
    函数名: createNonblockSock()
    返回值: 返回一个非阻塞的socket_fd
    参  数: 无
    函数作用: 返回一个非阻塞的socket_fd,失败的话,退出程序 
*/
int createNonblockSock()
{
    int listenfd = ::socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP);
    if(listenfd < 0)
    {
        printf("socket() error exit\n");
        exit(-1);
    }
    return listenfd;
}

/* 
    函数名: Socket(int fd)
    返回值: 无
    参  数: int fd, 文件标识符
    函数作用: Socket构造函数  
*/
Socket::Socket(int fd):fd_(fd)
{

}

/* 
    函数名: ~Socket(int fd)
    返回值: 无
    参  数: 无
    函数作用: Socket析构函数,关闭fd_  
*/
Socket::~Socket(){
    ::close(fd_);
}

/* 
    函数名: fd()
    返回值: 返回文件描述符fd_
    参  数: 无
    函数作用: 返回文件描述符
*/
int Socket::fd()const
{
    return fd_;
}

/* 
    函数名: ip()
    返回值: 返回ip地址
    参  数: 无
    函数作用: 返回ip地址
*/
std::string Socket::ip()const
{
    return ip_;
}

/* 
    函数名: port()
    返回值: 返回端口号
    参  数: 无
    函数作用: 返回端口号
*/
uint16_t Socket::port()const
{
    return port_;
}

/* 
    函数名: setReuseAddr(bool flag)
    返回值: 无
    参  数: bool flag, ture表示开启,false表示关闭
    函数作用: IP地址重复使用
*/
void Socket::setReuseAddr(bool flag){
    int opt = 1;
    ::setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &opt, static_cast<socklen_t>(sizeof(opt)));
}

/* 
    函数名: setReusePort(bool flag)
    返回值: 无
    参  数: bool flag, ture表示开启,false表示关闭
    函数作用: port重复使用
*/
void Socket::setReusePort(bool flag)
{   
    int opt = 1;
    ::setsockopt(fd_, SOL_SOCKET, SO_REUSEPORT, &opt, static_cast<socklen_t>(sizeof(opt)));
}

/* 
    函数名: setKeepAlive(bool flag)
    返回值: 无
    参  数: bool flag, ture表示开启,false表示关闭
    函数作用: SO_KEEPALIVE用于开启或者关闭保活探测，默认情况下是关闭的。
             当SO_KEEPALIVE开启时，可以保持连接检测对方主机是否崩溃，避免（服务器）永远阻塞于TCP连接的输入。
*/
void Socket::setKeepAlive(bool flag)
{   
    int opt = 1;
    ::setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, &opt, static_cast<socklen_t>(sizeof(opt)));
}

/* 
    函数名: setTcpNodelay(bool flag)
    返回值: 无
    参  数: bool flag, ture表示开启,false表示关闭
    函数作用: true,禁用Nagle算法,false不禁用
              Nagle 算法的基本定义是任一时刻，最多只能有一个未被确认的小段。
              所谓“小段”，指的是长度小于 MSS 尺寸的数据块，而未被确认则是指没有收到对方的 ACK 数据包。
              Nagle 算法的规则（参考 tcp_output.c 文件里 tcp_nagle_check 函数注释）：
              如果包长度达到 MSS，则允许发送；
              如果该数据包含有 FIN，则允许发送；
              设置了 TCP_NODELAY 选项，则允许发送；
              未设置 TCP_CORK 选项时，若所有发出去的小数据包（包长度小于 MSS）均被确认，则允许发送；
              上述条件都未满足，但发送了超时（一般为 200 ms），则立即发送。
              该算法的精妙之处在于它实现了自时钟（self-clocking）控制：ACK 返回得快，数据传输也越快。
              在相对高延迟的广域网中，更需要减少微型报的数目，该算法使得单位时间内发送的报文段数据更少。
              也就是说，RTT 控制着发包速率。
*/
void Socket::setTcpNodelay(bool flag)
{
    int opt = 1;
    ::setsockopt(fd_, SOL_SOCKET, TCP_NODELAY,  &opt, static_cast<socklen_t>(sizeof(opt)));
}

/* 
    函数名: bind(const InetAddress& servaddr)
    返回值: 无
    参  数: const InetAddress& servaddr,服务端网络配置信息
    函数作用: 绑定 (网络信息,sockfd)
*/
void Socket::bind(const InetAddress& servaddr)
{
    if(::bind(fd_, servaddr.addr(), sizeof(struct sockaddr)) < 0)
    {
        perror("bind error exit\n");
        close(fd_);
        exit(-1);
    }

    setIpAndPort(servaddr.ip(),servaddr.port());
}

/* 
    函数名: listen(int listenNum = 128)
    返回值: 无
    参  数: int listenNum = 128, 
    函数作用: 绑定 (网络信息,sockfd)
             listen 的第二个参数 backlog 指定了同时允许的最大半连接状态的连接数，
             也就确定了半连接队列的长度
*/
void Socket::listen(int listenNum)
{
    if(::listen(fd_, listenNum) != 0)
    {
        perror("listen error \n");
        close(fd_);
        exit(-1);
    }
}

/* 
    函数名: accept(InetAddress& clientaddr)
    返回值: 无
    参  数: InetAddress& clientaddr,地址信息类
    函数作用: 接受连接上来的客户端
*/
int Socket::accept(InetAddress& clientaddr)
{
    struct sockaddr_in peerAddr;
    socklen_t len = sizeof(peerAddr);
    int clientfd = ::accept4(fd_, (struct sockaddr*)&peerAddr, &len, SOCK_NONBLOCK | SOCK_NONBLOCK);
    clientaddr.setaddr(peerAddr);
    
    return clientfd;
}

/* 
    函数名: setIpAndPort(const std::string &ip, uint16_t port)
    返回值: 无
    参  数: const std::string &ip, ip地址; uint16_t port, 端口号
    函数作用: 为ip_, port_设置
*/
void Socket::setIpAndPort(const std::string &ip, uint16_t port)
{
    ip_ = ip;
    port_ = port;
}